package ni;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

import signals.*;

/**
 * UDPServer is really useful to receive datagrams from the net. Once launched
 * it allways listens and advice chatNI on reception of a Signal.
 *
 * @author gb
 */
public class UDPServer extends Thread {

    private DatagramSocket serverSocket;
    private boolean stop;
    private ChatNI chatNI;

    /**
     * Creates a UDPServer object (which also is a thread)
     *
     * @param chatNI doing the processing of the signals received
     * @param serverSocket socket on which Server will be listening
     */
    public UDPServer(ChatNI chatNI, DatagramSocket serverSocket) {
        this.chatNI = chatNI;
        this.serverSocket = serverSocket;
    }

    /**
     * Launch the server which is going to loop, executing : wait packet,
     * extract signal, tests it's type, call the appropriate chatNI function,
     * starts over
     */
    @Override
    public void run() {

        ChatNI.printDebug("UDPServer", "run", "Starting server on " + serverSocket.getLocalPort());

        this.stop = false;

        byte[] buf = new byte[Signal.MAX_SIZE];

        //Starting the loop on which the server is going to be
        //  wait packet
        //  extract signal
        //  tests it's type
        //  call the appropriate chatNI function
        //  starts over
        while (!stop) {
            DatagramPacket p = new DatagramPacket(buf, Signal.MAX_SIZE);

            ChatNI.printDebug("UDPServer", "run", "Waiting for a packet");

            //Waiting for the paquet
            try {
                this.serverSocket.receive(p);
            } catch (IOException ex) {
                System.err.println("In UDPServer : Could not receive a packet in UDP server");
                System.err.println(ex.getMessage());
                //If we could not receive the packet, this is probably because the ni killed our socket.
                //Hence no need to continue
                this.stop = true;
                continue;
            }

            ChatNI.printDebug("UDPServer", "run", "Received a packet");

            //Extracting the signal
            Object signal;
            try {
                signal = Signal.fromByteArray(buf);
            } catch (IOException ex) {
                System.err.println("In UDPServer : Could not retreive an object from the packet in UDP server");
                System.err.println(ex.getMessage());
                continue;
            } catch (ClassNotFoundException ex) {
                System.err.println("In UDPServer : Could not retreive an object from the packet in UDP server");
                System.err.println(ex.getMessage());
                continue;
            }

            //Testing the type of the signal and calling the appropriate chatNI function
            if (signal instanceof Goodbye) {
                ChatNI.printDebug("UDPServer", "run", "Received a Goodbye");
                chatNI.processGoodbye(((Goodbye) signal).getUsername());

            } else if (signal instanceof Hello) {
                ChatNI.printDebug("UDPServer", "run", "Received a Hello");
                Hello hello = (Hello) signal;
                chatNI.processHello(hello.getUsername());

            } else if (signal instanceof HelloOK) {
                ChatNI.printDebug("UDPServer", "run", "Received a HelloOK");
                chatNI.processHelloOK(((HelloOK) signal).getUsername());

            } else if (signal instanceof TextMessage) {
                ChatNI.printDebug("UDPServer", "run", "Received a TextMessage");
                TextMessage msg = (TextMessage) signal;
                chatNI.processTextMessage(msg.getMessage(), msg.getFrom(), msg.getTo());

            } else if (signal instanceof FileProposal) {
                ChatNI.printDebug("UDPServer", "run", "Received a Fileproposal");
                FileProposal fp = (FileProposal) signal;
                //Asks user whether he wants the file or not
                //Sends the right answer to the remote user from whom the proposal was.
                //This call to process file proposal blocks this server, therefore user should answer quickly
                //this problem was not happening the the proposal was done using tcp.
                if (chatNI.processFileProposal(fp.getFileName(), fp.getSize(), fp.getFrom(), fp.getTo())) {
                    chatNI.sendFileTransferAccepted(fp.getFileName(), fp.getSize(), fp.getFrom(), fp.getTo());
                } else {
                    chatNI.sendFileTransferNotAccepted(fp.getFileName(), fp.getSize(), fp.getFrom(), fp.getTo());
                }

            } else if (signal instanceof FileTransferAccepted) {
                ChatNI.printDebug("UDPServer", "run", "Received a FileTransferAccepted");
                FileTransferAccepted fta = (FileTransferAccepted) signal;
                chatNI.processFileTransferAccepted(fta.getFileName(), fta.getRemoteUsername());

            } else if (signal instanceof FileTransferNotAccepted) {
                ChatNI.printDebug("UDPServer", "run", "Received a FileTransferNotAccepted");
                FileTransferNotAccepted ftna = (FileTransferNotAccepted) signal;
                chatNI.processFileTransferNotAccepted(ftna.getFileName(), ftna.getRemoteUsername());

            } else {
                ChatNI.printDebug("UDPServer", "run", "Received a TextMessage");

            }

        }
    }

    /**
     * Close the socket so that if the server is waiting it has to stop
     */
    public void closeSockets() {
        this.serverSocket.close();
    }
}
